import os
import re
import yaml
from typing import Any, Dict
from .logger import logger

# 从当前脚本文件的目录向上跳两级，到达项目根目录，然后拼接 configs 目录
project_root = os.path.dirname(os.path.dirname(os.path.dirname(os.path.realpath(__file__))))
datapath = os.path.join(project_root, 'configs', 'static_data.yaml')


class FileRead:

    @staticmethod
    def read_data():
        f = open(datapath, encoding='utf8')
        data = yaml.safe_load(f)
        return data

    @staticmethod
    def write_data(data):
        """将修改后的数据写回到 YAML 文件"""
        with open(datapath, 'w', encoding='utf8') as f:
            yaml.safe_dump(data, f, allow_unicode=True)

    def update_data(self, key, value):
        """更新 YAML 文件中指定的键值对"""
        data = self.read_data()
        keys = key.split('.')
        current = data
        for k in keys[:-1]:
            current = current[k]
        current[keys[-1]] = value
        self.write_data(data)

    # 正则表达式匹配函数
    @staticmethod
    def match_regex(value: str, pattern: str) -> bool:
        """
        使用正则表达式匹配值
        :param value: 要匹配的字符串
        :param pattern: 正则表达式模式
        :return: 如果匹配成功则返回 True，否则返回 False
        """
        try:
            return re.match(pattern, value) is not None
        except Exception as e:
            raise ValueError(f"Invalid regex pattern: {pattern}") from e

    # def assert_response(self, expected, actual):
    #     """
    #     递归检查实际结果字典中是否包含预期结果字典中的所有键和值。
    #     只检查实际结果中存在的字段，忽略实际结果中不存在的字段。
    #
    #     :param expected: 预期的结果
    #     :param actual: 实际的结果
    #     :param case_name: 测试用例名称
    #     """
    #     # 检查预期结果和实际结果的数据类型是否一致
    #     if not isinstance(expected, type(actual)):
    #         logger.error(f"预期类型 {type(expected)} 与实际类型 {type(actual)} 不匹配。")
    #         assert False
    #
    #     if isinstance(expected, dict):
    #         for key, value in expected.items():
    #             # 只检查实际结果中存在的字段
    #             if key in actual:
    #                 # 递归检查值
    #                 self.assert_response(value, actual[key])
    #             else:
    #                 # 记录日志
    #                 logger.error(f"键 '{key}' 在实际响应中未找到")
    #                 assert False
    #     elif isinstance(expected, list):
    #         if len(expected) != len(actual):
    #             logger.error(f"预期列表长度 {len(expected)} 与实际列表长度 {len(actual)} 不匹配。")
    #             assert False
    #         for i, value in enumerate(expected):
    #             self.assert_response(value, actual[i])
    #     else:
    #         # 确保值完全相等
    #         if expected != actual:
    #             logger.error(f"预期值 '{expected}' 与实际值 '{actual}' 不匹配。")
    #             assert False

    def assert_response(self, expected, actual, current_path=""):
        """
        递归检查实际结果字典中是否包含预期结果字典中的所有键和值。
        只检查实际结果中存在的字段，忽略实际结果中不存在的字段。

        :param expected: 预期的结果
        :param actual: 实际的结果
        :param current_path: 当前路径，用于日志记录
        """
        # 检查预期结果和实际结果的数据类型是否一致
        if not isinstance(expected, type(actual)):
            logger.error(f"在路径 '{current_path}' 处，预期类型 {type(expected)} 与实际类型 {type(actual)} 不匹配。")
            assert False

        if isinstance(expected, dict):
            for key, value in expected.items():
                # 更新当前路径
                new_path = f"{current_path}.{key}" if current_path else key
                # 只检查实际结果中存在的字段
                if key in actual:
                    # 递归检查值
                    self.assert_response(value, actual[key], new_path)
                else:
                    # 记录日志
                    logger.error(f"在路径 '{current_path}' 处，键 '{key}' 在实际响应中未找到")
                    assert False
        elif isinstance(expected, list):
            # if len(expected) != len(actual):
            #     logger.error(f"在路径 '{current_path}' 处，预期列表长度 {len(expected)} 与实际列表长度 {len(actual)} 不匹配。")
            #     assert False
            for i, value in enumerate(expected):
                # 更新当前路径
                new_path = f"{current_path}[{i}]" if current_path else f"[{i}]"
                self.assert_response(value, actual[i], new_path)
        else:
            # 确保值完全相等
            if expected != actual:
                logger.error(f"在路径 '{current_path}' 处，预期值 '{expected}' 与实际值 '{actual}' 不匹配。")
                assert False

    @staticmethod
    def load_api_config(file_path):
        """加载 YAML 配置文件并返回配置内容"""
        # 获取当前文件的路径
        current_dir = os.path.dirname(os.path.abspath(__file__))
        # 向上回退到项目根目录
        project_root_dir = os.path.abspath(os.path.join(current_dir, '..', '..'))
        # 构建完整的文件路径
        full_file_path = os.path.join(project_root_dir, 'tests', 'test_data', file_path)

        with open(full_file_path, 'r', encoding='utf-8') as file:
            config = yaml.safe_load(file)
            return config

    # 从 YAML 文件加载断言条件
    @staticmethod
    def load_assertions(relative_path: str) -> Dict[str, Any]:
        # 获取当前文件的路径
        current_dir = os.path.dirname(os.path.abspath(__file__))
        # 向上回退到项目根目录
        project_root_dir = os.path.abspath(os.path.join(current_dir, '..', '..'))
        # 构建完整的文件路径
        full_file_path = os.path.join(project_root_dir, 'tests', 'test_data', relative_path)

        with open(full_file_path, 'r', encoding='utf-8') as f:
            return yaml.safe_load(f)

    # @staticmethod
    # def get_field_value(login_data, mappings):
    #     """根据字段名映射从登录数据中获取值"""
    #     for mapping_key in mappings:
    #         if mapping_key in login_data:
    #             # logger.info(f"使用映射字段: {mapping_key} -> {mappings}")
    #             return login_data[mapping_key]
    #
    #     # 如果没有找到匹配的字段，记录错误并返回 None
    #     logger.error(f"请求数据表跟字段映射表有字段没能对应上: {mappings}")
    #     return None


read_util = FileRead()
